package cn.ucox.web.ms.plugin;

import cn.ucox.web.ms.components.cache.TableMetaInfoCache;
import cn.ucox.web.ms.domain.StorageFile;
import cn.ucox.web.ms.persistence.Attachment;
import cn.ucox.web.ms.persistence.IAttachmentPersistence;
import cn.ucox.web.ms.persistence.SyncRowData;
import cn.ucox.web.ms.util.HttpClientUtil;
import cn.ucox.web.ms.util.Md5;
import com.alibaba.fastjson.JSONObject;
import com.github.tobato.fastdfs.domain.MateData;
import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.expression.Expression;
import org.springframework.jdbc.core.JdbcTemplate;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

/**
 * FastFS文件持久化组件
 *
 * @author chenw
 * @create 2017-07-28 23:29
 * @email javacspring@gmail.com
 */
public class FastFSFilePersistence implements IAttachmentPersistence {

    private static final Logger logger = LoggerFactory.getLogger(FastFSFilePersistence.class);

    private static final int SYNC_DATA_STRUCTURE_ACTION_INSERT = 1;
    private static final int SYNC_DATA_STRUCTURE_ACTION_UPDATE = 2;
    private static final int SYNC_DATA_STRUCTURE_ACTION_DELETE = 4;

    private static FastFileStorageClient storageClient;

    private JdbcTemplate jdbcTemplate;

    public FastFSFilePersistence(JdbcTemplate jdbcTemplate, FastFileStorageClient storageClient) {
        FastFSFilePersistence.storageClient = storageClient;
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void persistence(SyncRowData rowData) throws Exception {
        try {
            //获取主键
            TableMetaInfoCache cache = TableMetaInfoCache.getInstance();
            String pk = cache.getPk(rowData.getTab());

            JSONObject data = JSONObject.parseObject(rowData.getData());
            //获取主键值
            String val = data.getString(pk);
            //获取附件信息
            String attData = data.getString("__att_content__");
            //获取附件字段名称
            String column = data.getString("__att_col_name__");
            //上传附件并获取路径
            Attachment attachment = JSONObject.parseObject(attData, Attachment.class);

            // FS客户端上传文件 外网
            boolean flag = false;
            //新增只做上传文件
            if (SYNC_DATA_STRUCTURE_ACTION_INSERT == rowData.getAct()) {
                String upPath = this.uploadFileAndGetPath(attachment);
                if (null == upPath || "".equals(upPath)) {
                    logger.error("表{}主键{}值{}的附件上传失败", rowData.getTab(), pk, val);
                } else {
                    //调用接口进行FS文件上传     信息网
                    //String upPath = HttpClientUtil.doPostString(connectionPool.getmFastDFSConfig().getUploadUrl(),attachment);
                    //覆盖附件字段值为新的上传文件路径
                    data.put(column, upPath);
                    rowData.setData(data.toJSONString());
                    flag = true;
                }
            }
            //更新先删除文件在上传
            if (SYNC_DATA_STRUCTURE_ACTION_UPDATE == rowData.getAct()) {
                String filePath = this.findFilePath(new String[]{column, rowData.getTab(), pk, val});
                if (null == filePath || "".equals(filePath)) {
                    logger.error("表{}主键{}值{}的附件上传失败", rowData.getTab(), pk, val);
                }
                int count = this.deleteFileSec(filePath);
                if (count == 0) {
                    String upPath = this.uploadFileAndGetPath(attachment);
                    if (StringUtils.isNotBlank(upPath)) {
                        //调用接口进行FS文件上传     信息网
                        //String upPath = HttpClientUtil.doPostString(connectionPool.getmFastDFSConfig().getUploadUrl(),attachment);
                        //覆盖附件字段值为新的上传文件路径
                        data.put(column, upPath);
                        rowData.setData(data.toJSONString());
                        flag = true;
                    }
                    logger.info("表{}主键{}值{}附件{}删除成功", rowData.getTab(), pk, val, filePath);
                } else {
                    logger.error("表{}主键{}值{}附件{}删除失败", rowData.getTab(), pk, val, filePath);
                }
            }
            //删除只做删除文件
            if (SYNC_DATA_STRUCTURE_ACTION_DELETE == rowData.getAct()) {
                String filePath = this.findFilePath(new String[]{column, rowData.getTab(), pk, val});
                int count = this.deleteFileSec(filePath);
                if (count == 0) {
                    flag = true;
                    logger.info("表{}主键{}值{}附件{}删除成功", rowData.getTab(), pk, val, filePath);
                } else {
                    logger.error("表{}主键{}值{}附件{}删除失败", rowData.getTab(), pk, val, filePath);
                }
            }

            //数据持久化
            new PostgresDataPersistence(jdbcTemplate).persistence(rowData);

        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    //tab [0]附件字段 [1]表名 [2]主键字段 [3]主键值
    private String findFilePath(Object[] tab) throws Exception {
        if (tab == null || tab.length != 4) {
            return null;
        }
        String sql = String.format(" SELECT %s FROM %s WHERE %s = '%s' ", tab);
        return jdbcTemplate.queryForObject(sql, String.class);
    }


    //上传附件并获取路径
    private String uploadFileAndGetPath(Attachment attachment) throws Exception {
        String upPath = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            java.util.Date date = new java.util.Date();
            String dateStr = sdf.format(date);

            byte[] buf = Base64.getDecoder().decode(attachment.getContent());
            /*NameValuePair[] meta_list = new NameValuePair[5];
            meta_list[0] = new NameValuePair("name", attachment.getFileName());
            meta_list[1] = new NameValuePair("ext", attachment.getSuffix());
            meta_list[2] = new NameValuePair("time", dateStr);
            meta_list[3] = new NameValuePair("size", buf.length + "");
            meta_list[4] = new NameValuePair("md5", Md5.enMD5(buf));
            upPath = send(buf, attachment.getSuffix(), meta_list);*/

            Set<MateData> mateData = new HashSet<>();
            String md5 = Md5.enMD5(buf);
            mateData.add(new MateData("name", attachment.getFileName()));
            mateData.add(new MateData("ext", attachment.getSuffix()));
            mateData.add(new MateData("time", dateStr));
            mateData.add(new MateData("md5", md5));
            mateData.add(new MateData("size", buf.length+""));
            // 上传至FS
            upPath = sendSec(buf, attachment.getSuffix(), mateData);

            // FS文件元信息持久化
            StorageFile storageFile = new StorageFile(UUID.randomUUID().toString()
                    ,attachment.getFileName(),buf.length,
                    attachment.getSuffix(),upPath,md5);
            this.saveStorageFile(storageFile);
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
        return upPath;
    }


    private String sendSec(byte[] fileBuff, String fileExtName, Set<MateData> mateData) throws Exception {

        String upPath = null;
        StorePath storePath = null;
        InputStream inputStream = null;

        try {
            inputStream = new ByteArrayInputStream(fileBuff);
            storePath = storageClient.uploadFile(inputStream, (long) fileBuff.length, fileExtName, mateData);
            upPath = storePath.getFullPath();
        }finally {
            inputStream.close();
        }

        return upPath;
    }


    public int deleteFileSec(String fileId) throws Exception {
        int count = 0;
        storageClient.deleteFile(fileId);

        // FS文件元信息持久化
        this.deleteStorageFile(fileId);

        return count;
    }

    private int saveStorageFile(StorageFile storageFile){
        String sql = String.format("INSERT INTO t_fastdfs_file (guid,file_name,file_size,type,url,md5,upload_time,ref_num)" +
                "VALUES ('%s','%s',%s,'%s','%s','%s',localtimestamp,0)", storageFile.getGuid(),storageFile.getFileName(),
                storageFile.getSize(),storageFile.getType(),storageFile.getUrl(),storageFile.getMd5());
        logger.debug("保存FS文件元信息至数据库：{}",sql);
        int result = jdbcTemplate.update(sql);
        return result;
    }

    private int deleteStorageFile(String fileId){
        String sql = String.format("DELETE FROM t_fastdfs_file WHERE url = '%s'", fileId);
        logger.debug("删除FS文件元信息：{}",sql);
        int result = jdbcTemplate.update(sql);
        return result;
    }
}
